/*
 * Copyright (C) 2013 Manoel Afonso Filho
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package agimagem.concrete;

import agimagem.fitness.Fitness;
import agimagem.populacao.Individuo;
import agimagem.strategy.Mutacao;
import agimagem.util.AGChave;
import agimagem.util.Util;

/**
 * Implementação da Mutação Gaussiana. Se o indivíduo for sorteado para a sofrer
 * mutação, uma posição aleatória do mesmo é sorteada e o novo valor do gene
 * (byte) é calculado de acordo com a distribuição gaussiana.
 * <p>
 * O ponto de mutação (<code>P<sub>m</sub></code>) e o valor original do byte
 * (<code>B<sub>o</sub></code>) são armazenados na chave do indivíduo.
 * </p>
 * @author Manoel Afonso Filho
 */
public class ImgMutacao implements Mutacao {

    /**
     * Dois bits são selecionados e trocados de posição.
     * @param ind
     * @return
     */
    @Override
    public Individuo executar(Individuo ind) {
        // Verifica se ocorrerá mutação
        double p = Util.sr.nextDouble();
        if(p < Mutacao.taxaMutacao){
            byte[] imagem = ind.getCiphertext();
            // Sorteia um ponto do indivíduo para fazer a mutação
            int ponto = 0 + (int)(Util.sr.nextDouble() * ((imagem.length - 1) + 0));

            // Guarda o byte original
            byte byteOriginal = imagem[ponto];
            // Altera o byte
            byte byteAlterado = mutacaoGaussiana(byteOriginal);
            imagem[ponto] = byteAlterado;

            // Salva os valores do processo de mutação
            ind.setCiphertext(imagem);
            ind.getChaveDaGeracaoAtual().setMutacao((byte)ponto, byteOriginal);
//            ind.getChaveDaGeracaoAtual().setMutacao(ponto, byteOriginal); ORIGINAL
            ind.setFitness(Fitness.calcular(ind));
        }

        return ind;
    }

    /**
     * Calcula um valor gaussiano aleatório com desvio padrão igual a 1 e média
     * igual ao valor do byte no qual que se deseja fazer a mutação.
     * <p>
     * Assim, um byte com valor decimal igual 3, irá gerar uma gaussiana com
     * média 3 e desvio-padrão igual a 1 com os seguintes valores: <br/>
     * <code>[1, 2, 3, 4, 5]</code>
     * </p>
     * <p>
     * Se média igual a 127: <br/>
     * <code>[125, 126, 127, -128, -127]</code>
     * </p>
     * @param media Valor do byte que se deseja fazer a mutação.
     * @return Byte alterado.
     */
    private byte mutacaoGaussiana(byte media){
        byte val = (byte) (Util.sr.nextGaussian() * 1 + media);
        return val;
    }

    @Override
    public byte[] reverso(byte[] ciphertext, AGChave chave) {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

}
